<?php

namespace App\Kuafu\Order\Service;

use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Common\Service\CommonService;
use App\Kuafu\CouponExt\Enum\CouponExtEnum;
use App\Kuafu\CouponExt\Model\OrderCouponExtSplitModel;
use App\Kuafu\CouponPackage\Model\CouponPackageOrderModel;
use App\Kuafu\Order\Enum\CodeEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\Order\Jobs\OrderPayPolling;
use App\Kuafu\Order\Jobs\SubmitOrCancelOrderJob;
use App\Kuafu\Order\Model\ConsumerOrder;
use App\Kuafu\Order\Model\OrderCouponSplitModel;
use App\Kuafu\Order\Model\OrderSplitModel;
use App\Kuafu\Pay\BasePay;
use App\Kuafu\Pay\Enum\BasePayEnum;
use App\Kuafu\Pay\Service\PayService;
use Exception;
use Illuminate\Support\Facades\Log;

/**
 * 订单支付服务V1
 *
 * Class OrderPayService
 * @package App\Kuafu\Order\Service
 */
class OrderPayService extends BaseService
{
    /**
     * @var string 订单号
     */
    private $orderId;

    /**
     * @var \Illuminate\Database\Eloquent\Model|object|static|null 主订单详情
     */
    private $orderInfo;

    /**
     * @var int 订单支付方式
     */
    private $orderPayAction;

    /**
     * @var int 支付渠道
     */
    private $orderPayChannel;

    /**
     * @var string 付款码
     */
    private $orderAuthCode;

    /**
     * @var int 实际支付金额
     */
    private $payPrice = 0;

    private $userInfo = [
        'userId'   => 0,
        'openId'   => '',
        'platform' => 0,
        'unionid'  => ''
    ];

    public function __construct($brandId, $shopId, $orderId)
    {
        parent::__construct($brandId, $shopId);

        $this->orderPayAction  = CodeEnum::ORDER_PAY_ACTION_DEFAULT;
        $this->orderPayChannel = CodeEnum::ORDER_PAY_CHANNEL_MONEY;

        $this->orderId = $orderId;
        $this->setOrderInfo();

    }

    public function payChannel($channel)
    {
        if (!in_array($channel, CodeEnum::ORDER_PAY_CHANNEL)) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_DB_ADD, '订单支付 CHANNEL 不在规定的范围内');
        }
        $this->setPayChannel($channel);
        return $this;
    }

    /**
     * payAction
     *
     * @param int $actionId
     * @return $this
     * @throws OrderException
     */
    public function payAction(int $actionId)
    {
        if (!in_array($actionId, CodeEnum::ORDER_PAY_ACTION)) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_DB_ADD, '订单支付 Action 不在规定的范围内');
        }
        $this->setPayAction($actionId);
        return $this;
    }

    /**
     * 小程序订单支付
     *
     * @return array
     * @throws OrderException
     */
    public function genAppletPay()
    {
        $this->checkOrderHasPayed();

        $tmpOrder = new OrderBaseSplitService($this->brandId, $this->shopId, $this->orderId);
        $lock     = $tmpOrder->lockOrder(100, CodeEnum::ORDER_LOCK_TYPE_ORDER_PAY); // 2 分钟内不允许 再次支付

        // 订单已经上锁了
        if (!$lock) {
            $ttl = $tmpOrder->getLockTime(CodeEnum::ORDER_LOCK_TYPE_ORDER_PAY);
            throw new OrderException(OrderCodeEnum::ERR_ORDER_HAS_BAR_PAYING,
                OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_HAS_BAR_PAYING] . $ttl . '秒后再试');
        }

        // 创建支付
        try {
            switch ($this->orderPayChannel) {
                // 微信
                case CodeEnum::ORDER_PAY_CHANNEL_WECHAT:
                    $result = PayService::sMiniPay(
                        (int)$this->shopId,
                        (int)$this->orderId,
                        $this->orderInfo->shouldPrice,
                        '夸父点餐订单',
                        $this->userInfo['openId'] ?? ''
                    );
                    break;
                default:
                    throw new OrderException(OrderCodeEnum::ERR_ORDER_PAY_METHOD_ERR,
                        OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_PAY_METHOD_ERR]);
            }
            Log::info('用户下单支付数据', $result);
            OrderPayPolling::dispatch(
                $this->brandId,
                $this->shopId,
                $this->orderId,
                $result['payNo'],
            )->onQueue(OrderPayPolling::QUEUE_NAME)->delay(CodeEnum::ORDER_CANCEL_TIME_MINUS * 60);

            // 生成队列查询订单状态
        } catch (OrderException $exception) {
            Log::debug('订单支付失败');
            $tmpOrder->unLockOrder(CodeEnum::ORDER_LOCK_TYPE_ORDER_PAY);
        }

        return [
            'payConfig' => $result['wx']
        ];
    }

    // 订单成功支付成功
    public function success()
    {
        $this->checkOrderHasPayed();

        switch ($this->orderPayChannel) {
            case CodeEnum::ORDER_PAY_CHANNEL_ALIPAY:
            case CodeEnum::ORDER_PAY_CHANNEL_WECHAT:
                if (empty($this->payPrice)) {
                    $this->payPrice = $this->orderInfo->shouldPrice;
                }
                break;
            case CodeEnum::ORDER_PAY_CHANNEL_MONEY:
                $this->payPrice = $this->orderInfo->shouldPrice;
                break;
            case CodeEnum::ORDER_PAY_CHANNEL_TRY_TACK:
                // 现金支付
                $this->payPrice = 0;
                break;
            default:
                throw new OrderException(OrderCodeEnum::ERR_ORDER_PAY_METHOD_ERR,
                    OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_PAY_METHOD_ERR]);
        }

        // 获取 订单总支付优惠价格
        $sumCoupon = OrderCouponExtSplitModel::suffix($this->shopId)->query()->where('orderId', $this->orderId)
                                             ->where('couponStatus',  CouponExtEnum::COUPON_STATUS_USED)
                                             ->sum('userPay');

        if ($sumCoupon > 0) {
            $this->payPrice = bcadd($this->payPrice, $sumCoupon);
        }

        $this->orderInfo->orderStatus = CodeEnum::ORDER_STATUS_FINISHED;
        $this->orderInfo->payStatus   = CodeEnum::ORDER_PAY_STATUS_PAYED;

        // 更新订单支付方式
        $this->orderInfo->payChannel = $this->orderPayChannel;
        $this->finishOrder();
        return [
            'status' => BasePayEnum::TRADE_STATUS_OK
        ];
    }

    // 订单取消
    public function cancel(bool $forceCancel = false): array
    {
        if ($forceCancel || ($this->orderInfo->orderStatus == CodeEnum::ORDER_STATUS_WAIT_PAY &&
            $this->orderInfo->payStatus == CodeEnum::ORDER_PAY_STATUS_WAIT)) {

            $this->orderInfo->orderStatus = CodeEnum::ORDER_STATUS_CANCEL;
            $this->orderInfo->payStatus   = CodeEnum::ORDER_PAY_STATUS_CANCEL;

            // 优惠券返回
            OrderCouponSplitModel::suffix($this->shopId)->cancelOrderCoupon($this->orderId);
            // 如果是小程序的订单取消订单
            if ($this->orderInfo->userId) {
                ConsumerOrder::query()->updateOrCreate(
                    [
                        'orderId' => $this->orderInfo->orderId
                    ],
                    [
                        'orderStatus' => CodeEnum::ORDER_STATUS_CANCEL,
                        'payStatus'   => CodeEnum::ORDER_PAY_STATUS_CANCEL
                    ]
                );
            }

            $this->finishOrder();

            //取消订单 队列入队
            try {
                SubmitOrCancelOrderJob::dispatch(
                    $this->shopId,
                    $this->orderId,
                    SubmitOrCancelOrderJob::TYPE_CANCEL_ORDER,
                    SubmitOrCancelOrderJob::TYPE_CANCEL_TIMEOUT
                )->onQueue(SubmitOrCancelOrderJob::$QUEUE_NAME);
            } catch (Exception $ex) {
                CommonService::fsLogException(
                    '【 队列入队 】【 取消订单 】', $ex,
                    [
                        'shopId'  => $this->shopId,
                        'orderId' => $this->orderId,
                        'type'    => SubmitOrCancelOrderJob::TYPE_CANCEL_TIMEOUT
                    ],
                    true);
            }
        }
        return [
            'status' => BasePayEnum::TRADE_STATUS_OK
        ];
    }

    /**
     * 预退款 校验退款金额是否合法
     *
     * @param int $price
     * @return array
     */
    public function preRefund($price = 0)
    {
        if (bccomp($price, $this->orderInfo->payPrice) == 0) {
            $this->orderInfo->refundStatus = CodeEnum::ORDER_REFUND_STATUS_ALL;
        } elseif (bccomp($price, $this->orderInfo->payPrice) < 0) {
            $this->orderInfo->refundStatus = CodeEnum::ORDER_REFUND_STATUS_PART;
        } else {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_REFUND_MONEY_ERROR);
        }
    }

    /**
     * 实际退款接口
     *
     * @param int $price
     * @return array
     */
    public function refund($price = 0)
    {
        $this->payPrice               = bcsub($this->orderInfo->payPrice, $price);
        $this->orderInfo->refundMoney = bcadd($this->orderInfo->refundMoney, $price);

        $this->finishOrder();
        return [
            'status' => BasePayEnum::TRADE_STATUS_OK
        ];
    }

    /**
     * @return $this|\Illuminate\Database\Eloquent\Model|object|null
     */
    public function getOrderInfo()
    {
        return $this->orderInfo;
    }

    /**
     * 更新订单主表
     *
     * @return $this
     */
    public function updateOrderPayInfo(): OrderPayService
    {
        $orderInfo             = OrderSplitModel::suffix($this->shopId)->query()->where('orderId', $this->orderId)
                                                ->first();
        $orderInfo->platformId = $this->userInfo['unionid'];
        $orderInfo->userId     = $this->userInfo['userId'];
        $orderInfo->save();
        return $this;
    }


    /**
     * 根据支付表中的 PayChannel 转换为 Order表的 Channel
     * @param $payActionId
     * @return $this
     * @throws OrderException
     */
    public function switchPayChannel($payChannel)
    {
        return $this->payChannel($payChannel);
    }

    /**
     * 实际支付金额
     * @param int $payPrice
     */
    public function setPayPrice(int $payPrice)
    {
        $this->payPrice = $payPrice;
        return $this;
    }

    /**
     * 设置用户信息
     * @param array $data
     */
    public function setUserInfo(array $data)
    {
        $this->userInfo = [
            'userId'     => $data['userId'] ?? 0,
            'openId'     => $data['openId'] ?? '',
            'platformId' => $data['platformId'] ?? 0,
            'unionid'    => $data['unionid'] ?? ''
        ];
    }

    /**
     * 设置付款码
     *
     * @param string $payAuthCode
     * @return $this
     */
    public function setAuthCode(string $payAuthCode)
    {
        $this->orderPayChannel = BasePay::checkAuthCode($payAuthCode);
        $this->orderAuthCode   = $payAuthCode;

        return $this;
    }

    /**
     * @param string $orderId
     */
    public function setOrderId(string $orderId)
    {
        $this->orderId = $orderId;
    }

    /**
     * 订单结束
     */
    private function finishOrder()
    {
        $this->orderInfo->payPrice     = $this->payPrice;
        $this->orderInfo->completeTime = time();
        $this->orderInfo->save();
    }

    /**
     * @param int $channel
     */
    private function setPayChannel(int $channel): void
    {
        $this->orderPayChannel = $channel;
    }

    /**
     * @param int $actionId
     */
    private function setPayAction(int $actionId): void
    {
        $this->orderPayAction = $actionId;
    }

    /**
     * 订单详情
     */
    private function setOrderInfo()
    {
        $this->orderInfo = OrderSplitModel::suffix($this->shopId)->query()->where('orderId', $this->orderId)->first();

        if (empty($this->orderInfo)) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_ID_NOT_EXIST,
                OrderCodeEnum::CODE_TEXT[OrderCodeEnum::ERR_ORDER_ID_NOT_EXIST]);
        }

        $this->orderPayAction  = $this->orderInfo->payAction;
        $this->orderPayChannel = $this->orderInfo->orderPayChannel;

        if (empty($this->shopId) && empty($this->brandId)) {
            $this->shopId  = $this->orderInfo->shopId;
            $this->brandId = $this->orderInfo->brandId;
        }
    }


    public static function getMonthIncome($shopId)
    {
        try {
            //获取上个月开始时间到结束时间的时间戳
            $lastMonthIncome               = BaseService::getMonthStartToEnd();
            $totalPrice['lastMonthIncome'] = OrderSplitModel::suffix($shopId)->getTotalIncomeByTime($lastMonthIncome);
            return $totalPrice;
        } catch (Exception $exception) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_COUNT_MONTH_PRICE, '', $exception);
        }
    }

    /**
     * 获取本月截止当前的实际收入
     * @throws OrderException
     */
    public static function getMonthToDayActualIncome($shopId)
    {
        try {
            //本月开始到现在的时间戳
            $timeToDay = [
                strtotime(date('Y-m-1')),
                time()
            ];
            return OrderSplitModel::suffix($shopId)->getTotalIncomeByTime($timeToDay);
        } catch (Exception $exception) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_COUNT_MONTH_PRICE, '', $exception);
        }
    }
}
